/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.bff.slimserver.musicjukebox.ui.components;

import com.bff.bjj.ui.component.MusicList;
import com.bff.bjj.ui.component.events.KeyTypedEvent;
import com.bff.bjj.ui.component.events.KeyTypedListener;
import com.bff.slimserver.music.exceptions.PlaylistException;
import com.bff.slimserver.musicjukebox.lib.JukeboxProperties;
import com.bff.slimserver.musicjukebox.lib.actions.PlaylistAddYearAction;
import com.bff.slimserver.musicjukebox.lib.actions.PlaylistInsertYearAction;
import com.bff.slimserver.musicjukebox.lib.actions.PlaylistLoadYearAction;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.DefaultListModel;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import org.bff.slimserver.SlimDatabase;
import org.bff.slimserver.SlimPlaylist;
import org.bff.slimserver.exception.SlimConnectionException;

/**
 *
 * @author Bill
 */
public class YearList extends MusicList implements MouseListener {

    private SlimDatabase database;
    private DefaultListModel yearListModel;
    private JPopupMenu yearMenu;
    private SlimPlaylist playlist;
    private List<String> years;
    private static final int KEY_TIMEOUT = 2000;
    public boolean keyTyping;
    private long lastKeyTime = 0;
    private List<KeyTypedListener> keyTypedListeners;
    private int initial;

    public YearList() {
        years = new ArrayList<String>();
        keyTypedListeners = new ArrayList<KeyTypedListener>();

        yearListModel = new DefaultListModel();
        try {
            setDatabase(JukeboxProperties.getInstance().getDatabase());
            setPlaylist(JukeboxProperties.getInstance().getPlaylist());
        } catch (SlimConnectionException ex) {
            Logger.getLogger(YearList.class.getName()).log(Level.SEVERE, null, ex);
        } catch (PlaylistException ex) {
            Logger.getLogger(YearList.class.getName()).log(Level.SEVERE, null, ex);
        }
        setCellRenderer(new YearListCellRenderer());
        addMouseListener(this);

        KeyListener[] keyListeners = getKeyListeners();

        for (int i = 0; i < keyListeners.length; i++) {
            removeKeyListener(keyListeners[i]);
        }

        addKeyListener(new ListSearcher());
    }

    public YearList(List<String> years) {
        this();
        listYears(years);
    }

    public void setYears(List<String> years) {
        listYears(years);
    }

    private void listYears(List<String> years) {
        this.years = new ArrayList<String>(years);
        Collections.sort(years);
        this.years.remove("0");
        loadList();
    }

    public void listAll() {
        listYears(new ArrayList<String>(getDatabase().getYears()));
    }

    public void filterList(String filter) {
        try {
            yearListModel.clear();
            //artistListModel.addElement(new String("All Artists"));

            for (String year : years) {
                if (year.toLowerCase().startsWith(filter.toLowerCase())) {
                    yearListModel.addElement(year);
                }
            }

            setModel(yearListModel);
            setSelectedIndex(0);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void clearFilter() {
        loadList();
    }

    public String getSelectedYear() {
        if (getSelectedValue() == null) {
            return null;
        }

        return (String) getSelectedValue();
    }

    public SlimDatabase getDatabase() {
        return database;
    }

    public void setDatabase(SlimDatabase database) {
        this.database = database;
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        createYearPlayPopUp(e);
    }

    @Override
    public void mousePressed(MouseEvent e) {
        createYearPlayPopUp(e);
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        createYearPlayPopUp(e);
    }

    @Override
    public void mouseEntered(MouseEvent e) {
        //throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public void mouseExited(MouseEvent e) {
        //throw new UnsupportedOperationException("Not supported yet.");
    }

    private void createYearPlayPopUp(MouseEvent e) {
        if (e.isPopupTrigger()) {
            setSelectedIndex(locationToIndex(e.getPoint()));
             String year = getSelectedYear();
            yearMenu = new JPopupMenu();
            yearMenu.add(new JMenuItem(new PlaylistAddYearAction(getPlaylist(), year)));
            yearMenu.add(new JMenuItem(new PlaylistInsertYearAction(getPlaylist(), year)));
            yearMenu.add(new JMenuItem(new PlaylistLoadYearAction(getPlaylist(), year)));
            yearMenu.setVisible(true);
            yearMenu.show(e.getComponent(), e.getX(), e.getY());
        }
    }

    public SlimPlaylist getPlaylist() {
        return playlist;
    }

    public void setPlaylist(SlimPlaylist playlist) {
        this.playlist = playlist;
    }

    private void loadList() {
        try {
            yearListModel.clear();
            //artistListModel.addElement(new String("All Artists"));

            for (String year : years) {
                yearListModel.addElement(year);
            }

            setModel(yearListModel);
            setSelectedIndex(0);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public boolean isKeyTyping() {
        return keyTyping;
    }

    private void setKeyTyping(boolean b) {
        this.keyTyping = b;

        if (b) {
            fireKeyTypedEvent(KeyTypedEvent.TYPE_EVENT.TYPE_STARTED, false);
        } else {
            fireKeyTypedEvent(KeyTypedEvent.TYPE_EVENT.TYPE_ENDED, initial != getSelectedIndex());
        }
    }

    public long getLastKeyTime() {
        return lastKeyTime;
    }

    public void setLastKeyTime(long lastKeyTime) {
        this.lastKeyTime = lastKeyTime;
    }

    private class ListSearcher extends KeyAdapter {

        protected String m_key = "";

        public ListSearcher() {
        }

        public void keyTyped(KeyEvent e) {
            if (!isKeyTyping()) {
                initial = getSelectedIndex();
                setKeyTyping(true);
                new Thread(new KeyTimeThread()).start();
            }

            char ch = e.getKeyChar();
            if (!Character.isLetterOrDigit(ch)) {
                return;
            }

            if (getLastKeyTime() + KEY_TIMEOUT < System.currentTimeMillis()) {
                m_key = "";
            }

            setLastKeyTime(System.currentTimeMillis());

            m_key += Character.toLowerCase(ch);

            for (int k = 0; k < yearListModel.getSize(); k++) {
                String year = ((String) yearListModel.getElementAt(k));
                if (year.toLowerCase().startsWith(m_key)) {
                    setSelectedIndex(k);
                    ensureIndexIsVisible(k);
                    break;
                }
            }
        }
    }

    private class KeyTimeThread implements Runnable {

        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException ex) {
                    Logger.getLogger(YearList.class.getName()).log(Level.SEVERE, null, ex);
                }

                if (getLastKeyTime() + KEY_TIMEOUT < System.currentTimeMillis()) {
                    setKeyTyping(false);
                    break;
                }
            }
        }
    }

    private void fireKeyTypedEvent(KeyTypedEvent.TYPE_EVENT event, boolean change) {
        for (KeyTypedListener listener : keyTypedListeners) {
            listener.keyTyped(new KeyTypedEvent(this, event, change));
        }
    }

    public void addKeyTypedListener(KeyTypedListener listener) {
        keyTypedListeners.add(listener);
    }

    public void removeKeyTypedListener(KeyTypedListener listener) {
        keyTypedListeners.remove(listener);
    }
}